package com.jacky.iot;

import com.jacky.iot.resources.ApiV1Resource;
import com.jacky.iot.resources.DeviceAwareObservableResource;
import com.jacky.iot.resources.ObservableResource;
import com.jacky.iot.resources.TemperatureResource;
import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.config.CoapConfig;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.Endpoint;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.elements.config.*;
import org.eclipse.californium.elements.util.NamedThreadFactory;
import org.eclipse.californium.elements.util.SslContextUtil;
import org.eclipse.californium.scandium.DTLSConnector;
import org.eclipse.californium.scandium.config.DtlsConfig;
import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
import org.eclipse.californium.scandium.dtls.CertificateType;
import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
import org.eclipse.californium.scandium.dtls.x509.SingleCertificateProvider;
import org.eclipse.californium.scandium.dtls.x509.StaticNewAdvancedCertificateVerifier;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.util.Collections;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


public class StartUpCoapServer extends CoapServer {


    static {
        CoapConfig.register();
        UdpConfig.register();
        DtlsConfig.register();
        TcpConfig.register();
        System.setProperty("COAP_ROOT_RESOURCE_NOTE",
                "Note: the data sent to this server is public visible to other\n" +
                        "      users! Don't send data, which requires data privacy.");
    }

    private static final File CONFIG_FILE = new File("CaliforniumPlugtest3.properties");
    private static final String CONFIG_HEADER = "Californium CoAP Properties file for AppServer Server";

    private static final int DEFAULT_MAX_RESOURCE_SIZE = 8192;
    private static final int DEFAULT_BLOCK_SIZE = 64;

    private NewAdvancedCertificateVerifier certificateVerifier;
    DeviceAwareObservableResource observerResource;
    ObservableResource observableResource;
    static CoapServer coapServer;

    private static final Configuration.DefinitionsProvider DEFAULTS = new Configuration.DefinitionsProvider() {

        @Override
        public void applyDefinitions(Configuration config) {
            config.set(DtlsConfig.DTLS_CONNECTION_ID_LENGTH, 6);
            config.set(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, false);
        }
    };

    public static void main(String[] args) throws IOException, GeneralSecurityException {
        DeviceAwareObservableResource resource = new DeviceAwareObservableResource("observable");
        Configuration configuration = init("127.0.0.1", 5683);
        coapServer.add(resource);
        coapServer.start();


//        TimeUnit.SECONDS.sleep(30);
//        System.out.println("---------------");
//        resource.notifyDevice("dev123","设备消息回调");
//        TimeUnit.SECONDS.sleep(3);
//        resource.notifyDevice("dev123","设备第二次消息回调");
    }

    public StartUpCoapServer(Configuration config) {
        // 这是支持observable的关键
        super(config);
        add(new ObservableResource("obs"));
    }

    private static Configuration init(String host, Integer port) throws IOException, GeneralSecurityException {
//        Configuration netCfg = new Configuration();
//        netCfg.set(CoapConfig.BLOCKWISE_STRICT_BLOCK2_OPTION, Boolean.TRUE);
//        netCfg.set(CoapConfig.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, Boolean.TRUE);
//        netCfg.set(CoapConfig.BLOCKWISE_STATUS_LIFETIME, 300, TimeUnit.SECONDS);
//        netCfg.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
//        netCfg.set(CoapConfig.RESPONSE_MATCHING, CoapConfig.MatcherMode.RELAXED);
//        netCfg.set(CoapConfig.PREFERRED_BLOCK_SIZE, 1024);
//        netCfg.set(CoapConfig.MAX_MESSAGE_SIZE, 1024);
//        netCfg.set(CoapConfig.MAX_RETRANSMIT, 4);
//        netCfg.set(CoapConfig.COAP_PORT, port);
//        CoapServer coapServer = new CoapServer(netCfg);

        Configuration configuration = Configuration.createWithFile(CONFIG_FILE, CONFIG_HEADER, new Configuration.DefinitionsProvider() {
            @Override
            public void applyDefinitions(Configuration config) {
                config.set(SystemConfig.HEALTH_STATUS_INTERVAL, 300, TimeUnit.SECONDS);
                config.set(CoapConfig.MAX_RESOURCE_BODY_SIZE, DEFAULT_MAX_RESOURCE_SIZE);
                config.set(CoapConfig.MAX_MESSAGE_SIZE, DEFAULT_BLOCK_SIZE);
                config.set(CoapConfig.PREFERRED_BLOCK_SIZE, DEFAULT_BLOCK_SIZE);
                config.set(CoapConfig.NOTIFICATION_CHECK_INTERVAL_COUNT, 4);
                config.set(CoapConfig.NOTIFICATION_CHECK_INTERVAL_TIME, 30, TimeUnit.SECONDS);
                config.set(CoapConfig.TCP_NUMBER_OF_BULK_BLOCKS, 1);
                config.set(CoapConfig.MAX_ACTIVE_PEERS, 10000);
                config.set(DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY, false);
                config.set(DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT, null, TimeUnit.SECONDS);
                config.set(DtlsConfig.DTLS_CONNECTION_ID_LENGTH, 6);
//            config.set(DtlsConfig.DTLS_PRESELECTED_CIPHER_SUITES, PRESELECTED_CIPHER_SUITES);
                config.set(DtlsConfig.DTLS_MAX_CONNECTIONS, 10000);
                config.set(DtlsConfig.DTLS_REMOVE_STALE_DOUBLE_PRINCIPALS, false);
                config.set(DtlsConfig.DTLS_MAC_ERROR_FILTER_QUIET_TIME, 4, TimeUnit.SECONDS);
                config.set(DtlsConfig.DTLS_MAC_ERROR_FILTER_THRESHOLD, 8);
                config.set(DtlsConfig.DTLS_RETRANSMISSION_TIMEOUT, 3, TimeUnit.SECONDS);
                config.set(DtlsConfig.DTLS_ADDITIONAL_ECC_TIMEOUT, 8, TimeUnit.SECONDS);
                config.set(TcpConfig.TCP_CONNECT_TIMEOUT, 15, TimeUnit.SECONDS);
                config.set(TcpConfig.TCP_CONNECTION_IDLE_TIMEOUT, 60, TimeUnit.MINUTES);
                config.set(TcpConfig.TLS_HANDSHAKE_TIMEOUT, 60, TimeUnit.SECONDS);
            }
        });
        coapServer = new StartUpCoapServer(configuration);
        CoapEndpoint.Builder noSecCoapEndpointBuilder = new CoapEndpoint.Builder();
        InetAddress addr = InetAddress.getByName(host);
        InetSocketAddress sockAddr = new InetSocketAddress(addr, port);
        noSecCoapEndpointBuilder.setInetSocketAddress(sockAddr);

        noSecCoapEndpointBuilder.setConfiguration(configuration);
        CoapEndpoint noSecCoapEndpoint = noSecCoapEndpointBuilder.build();
        coapServer.addEndpoint(noSecCoapEndpoint);

        if (isSSLEnable()) {
            /*CoapEndpoint.Builder dtlsCoapEndpointBuilder = new CoapEndpoint.Builder();
            DtlsConnectorConfig dtlsConnectorConfig = this.dtlsConnectorConfig(netCfg);
            netCfg.set(CoapConfig.COAP_SECURE_PORT, dtlsConnectorConfig.getAddress().getPort());
            dtlsCoapEndpointBuilder.setConfiguration(netCfg);
            DTLSConnector connector = new DTLSConnector(dtlsConnectorConfig);
            dtlsCoapEndpointBuilder.setConnector(connector);
            CoapEndpoint dtlsCoapEndpoint = dtlsCoapEndpointBuilder.build();
            coapServer.addEndpoint(dtlsCoapEndpoint);
            certificateVerifier = dtlsConnectorConfig.getAdvancedCertificateVerifier();
            ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("CoapServer#"));
            executor.scheduleAtFixedRate(this::evictTimeoutSessions, new Random().nextInt((int) getDtlsSessionReportTimeout()), getDtlsSessionReportTimeout(), TimeUnit.MILLISECONDS);*/
        }

//        Resource root = coapServer.getRoot();
//        CustomerMessageDeliverer messageDeliverer = new CustomerMessageDeliverer(root, configuration);
//        coapServer.setMessageDeliverer(messageDeliverer);
//
//        CoapResource apiResource = new CoapResource("api");
//        apiResource.add(new ApiV1Resource("v1"));
//        coapServer.add(new TemperatureResource("temperature"));
//        coapServer.add(apiResource);

//        observerResource = new DeviceAwareObservableResource("observer");
//        observerResource.setObservable(true); // 设置为可观察
//        observerResource.getAttributes().setObservable();
//        coapServer.add(observerResource);

//        observableResource = new ObservableResource("observable");
//        observableResource.setObservable(true);
//        observableResource.getAttributes().setObservable();
//        coapServer.add(observableResource);
//        coapServer.start();
        return configuration;
    }


    private static Boolean isSSLEnable() {
        return Boolean.FALSE;
    }

    /*public DtlsConnectorConfig dtlsConnectorConfig(Configuration configuration) throws UnknownHostException {
        DtlsConnectorConfig.Builder configBuilder = new DtlsConnectorConfig.Builder(configuration);
        configBuilder.setAddress(getInetSocketAddress());
        SslCredentials sslCredentials = this.coapDtlsCredentialsConfig.getCredentials();
        SslContextUtil.Credentials serverCredentials =
                new SslContextUtil.Credentials(sslCredentials.getPrivateKey(), null, sslCredentials.getCertificateChain());
        configBuilder.set(DtlsConfig.DTLS_ROLE, DtlsConfig.DtlsRole.SERVER_ONLY);
        configBuilder.set(DtlsConfig.DTLS_CLIENT_AUTHENTICATION_MODE, CertificateAuthenticationMode.WANTED);
        configBuilder.setAdvancedCertificateVerifier(
                new TbCoapDtlsCertificateVerifier(
                        transportService,
                        serviceInfoProvider,
                        dtlsSessionInactivityTimeout,
                        dtlsSessionReportTimeout,
                        skipValidityCheckForClientCert
                )
        );
        configBuilder.setCertificateIdentityProvider(new SingleCertificateProvider(serverCredentials.getPrivateKey(), serverCredentials.getCertificateChain(),
                Collections.singletonList(CertificateType.X_509)));
        return configBuilder.build();
    }

    private void evictTimeoutSessions() {
        certificateVerifier.evictTimeoutSessions();
    }

    private long getDtlsSessionReportTimeout() {
        return certificateVerifier.getDtlsSessionReportTimeout();
    }

    private InetSocketAddress getInetSocketAddress() throws UnknownHostException {
        InetAddress addr = InetAddress.getByName(host);
        return new InetSocketAddress(addr, port);
    }*/
}
